package com.hk.core.autoconfigure.authentication.security.servlet;

import com.hk.commons.util.ArrayUtils;
import com.hk.commons.util.StringUtils;
import com.hk.core.authentication.api.SecurityContextUtils;
import com.hk.core.autoconfigure.message.websocket.WebSocketProperties;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.messaging.Message;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.scheduling.concurrent.DefaultManagedTaskScheduler;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.config.annotation.web.socket.AbstractSecurityWebSocketMessageBrokerConfigurer;
import org.springframework.security.config.annotation.web.socket.EnableWebSocketSecurity;
import org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager;
import org.springframework.security.messaging.context.SecurityContextChannelInterceptor;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration;
import org.springframework.web.socket.handler.WebSocketHandlerDecoratorFactory;
import org.springframework.web.socket.server.HandshakeHandler;

import java.util.concurrent.TimeUnit;

/**
 * <pre>
 * spring security websocket 配置类
 * {@link SecurityContextUtils#getPrincipal()} 获取用户信息
 * </pre>
 *
 * @author kevin
 */
@AutoConfiguration
@EnableWebSocketSecurity
@EnableWebSocketMessageBroker
@EnableConfigurationProperties(WebSocketProperties.class)
@ConditionalOnClass(value = {SecurityContextChannelInterceptor.class})
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public class WebSocketSecurityConfigurer
        /*extends AbstractSecurityWebSocketMessageBrokerConfigurer*/
        implements WebSocketMessageBrokerConfigurer {

    private final WebSocketProperties webSocketProperties;

    private final WebSocketHandlerDecoratorFactory webSocketHandlerDecoratorFactory;

//	/**
//	 * channel 拦截器
//	 */
//	private List<ChannelInterceptor> channelInterceptors;

    /**
     * 握手回调
     */
    private final HandshakeHandler handshakeHandler;

    public WebSocketSecurityConfigurer(WebSocketProperties webSocketProperties,
                                       ObjectProvider<WebSocketHandlerDecoratorFactory> decoratorFactories,
//			ObjectProvider<List<ChannelInterceptor>> channelInterceptors,
                                       ObjectProvider<HandshakeHandler> handshakeHandler) {
        this.webSocketProperties = webSocketProperties;
        this.webSocketHandlerDecoratorFactory = decoratorFactories.getIfAvailable();
//		this.channelInterceptors = channelInterceptors.getIfAvailable();
        this.handshakeHandler = handshakeHandler.getIfAvailable();
    }

    /**
     * 配置消息的权限，这里配置任意消息都需要认证，包括建立连接时
     * <p>
     * 配合 @EnableWebSocketSecurity 一起使用，{@link  AbstractSecurityWebSocketMessageBrokerConfigurer} 已过时
     */
    @Bean
    public AuthorizationManager<Message<?>> authorizationManager(MessageMatcherDelegatingAuthorizationManager.Builder messages) {
        return messages
                .anyMessage().permitAll()
                .build();
    }

//    /**
//     * 配置 {@link ChannelRegistration}，可以添加自定义拦截器
//     *
//     * @param registration registration
//     */
//    @Override
//    protected void customizeClientInboundChannel(ChannelRegistration registration) {
////        registration.interceptors()
//    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint(webSocketProperties.getEndpoint())
                .setHandshakeHandler(handshakeHandler)
                .setAllowedOrigins(webSocketProperties.getAllowedOrigins())
                .withSockJS()
                .setWebSocketEnabled(true)
                .setSessionCookieNeeded(true)
                .setHeartbeatTime(TimeUnit.SECONDS.toMillis(25))
                .setDisconnectDelay(TimeUnit.SECONDS.toMillis(5))
//                .setInterceptors()// 添加握手拦截器,可以在握手之前处理,握手之后处理
        ;
    }


    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        var scheduler = new DefaultManagedTaskScheduler();
        registry.enableSimpleBroker(webSocketProperties.getSimpleBrokers()).setTaskScheduler(scheduler);
        var applicationDestinationPrefixes = webSocketProperties.getApplicationDestinationPrefixes();
        if (ArrayUtils.isNotEmpty(applicationDestinationPrefixes)) {
            registry.setApplicationDestinationPrefixes(applicationDestinationPrefixes);
        }
        var userDestinationPrefix = webSocketProperties.getUserDestinationPrefix();
        if (StringUtils.isNotEmpty(userDestinationPrefix)) {
            registry.setUserDestinationPrefix(userDestinationPrefix);
        }
        var cacheLimit = webSocketProperties.getCacheLimit();
        if (null != cacheLimit) {
            registry.setCacheLimit(cacheLimit);
        }
    }

//    /**
//     * 配置消息的权限，这里配置任意消息都需要认证，包括建立连接时
//     *
//     * @param messages messages
//     */
//    @Override
//    protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
////        messages.anyMessage().authenticated();
//        messages.anyMessage().permitAll();
////        messages.simpTypeMatchers(SimpMessageType.CONNECT).permitAll(); //连接时不需要认证
////        messages.simpDestMatchers("/user/**").permitAll(); // 发送到 /user/** 的不需要认证
//    }

//    /**
//     * 是否支持 csrf, 默认为 false 如果配置为 false ：会添加 {@link CsrfChannelInterceptor} 拦截器
//     *
//     * @return true
//     * @see AbstractSecurityWebSocketMessageBrokerConfigurer#configureClientInboundChannel(ChannelRegistration)
//     */
//    @Override
//    protected boolean sameOriginDisabled() {
//        return webSocketProperties.isOriginDisabled();
//    }

    @Override
    public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
        if (this.webSocketHandlerDecoratorFactory != null) {
            registration.addDecoratorFactory(webSocketHandlerDecoratorFactory);
        }
    }
}
